這篇會介紹用 Python 寫簡單的 ROS 節點,在之後寫功能更複雜一點的通訊節點還有控制無人機的程式時,就會根據這一篇的結構來延伸和擴充。
ROS 的節點(Node)是 ROS 通訊網路的基本單位,每一個執行特定功能(如讀取感測器資料、控制馬達轉速等)的程序會被視為一個節點。
一個套件內通常會包含多個節點,ROS 支援一個套件內存在不同的程式語言寫的節點(常見的有 Python、C++),再藉由 ROS 系統串接形成一個完整功能的套件。
使用 rospy
函式庫,就能用 Python 來編寫 ROS 的節點。
回到上一篇建好的 my_package/src
目錄,在這裡示範建立一個節點 create_node.py
,程式內容如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import rospy
import random
def simple_node():
# 初始化ROS節點
rospy.init_node('simple_node', anonymous=True)
# 設定頻率,這裡設定為1Hz
rate = rospy.Rate(1)
# 檢查節點是否被關閉
while not rospy.is_shutdown():
# 輸出日誌訊息
hello_str = f"Hello ROS world {random.randint(1, 10)}"
rospy.loginfo(hello_str)
rospy.loginfo("Node is running...")
# 控制循環頻率
rate.sleep()
if __name__ == '__main__':
try:
simple_node()
# 捕捉程序中斷異常並安全退出
except rospy.ROSInterruptException:
pass
這是一個節點常見的程式結構:
節點初始化(Node Initialization):
任何節點開始運作之前都必須先初始化,它會向主節點(Master)註冊自己的名稱,以便日後與其他節點進行通訊。
rospy.init_node('simple_node', anonymous=True)
這行就是用來初始化這個節點,同時也會設定節點名稱,我先取名為 simple_node
。
在 ROS 系統中,節點的通訊是透過彼此的名稱來找到對方,因此每一個節點都必須是獨一無二的名稱。如果不想費盡心思取名,就可以在初始化時將
anonymous
參數設為True
,那麼 ROS 系統會自動為這個節點名稱後面加上一串隨機生成的數字。
節點操作:
在這個示範程式中使用了幾個常用的函式:
節點是否被關閉:rospy.is_shutdown()
這個函式是代表當前節點是否被中止。
設定頻率和休眠:rate = rospy.Rate(1)
是設定循環頻率 1 Hz,搭配休眠函式 rate.sleep()
維持程式執行速度輸出訊息。另一種常見的暫停方法是使用 rospy.sleep(time)
,當程式執行到這裡時會強制休眠指定時間。
輸出日誌訊息:rospy.loginfo()
這行將會在終端機輸出訊息。
在 Day 3 提過主節點(Master)是一種特殊節點,它負責維護一個名稱服務(Name Service)的表格,裡面儲存每一個節點的名字,以便他們找到彼此並建立連結。
假如我們直接執行剛才寫好的 create_node.py
,會得到類似以下的錯誤訊息:
$ python3 create_node.py
Unable to register with master node [http://localhost:11311]: master may not be running yet. Will keep trying.
這是在說主節點(Master)尚未啟動, ROS 系統無法向主節點註冊節點 simple_node
。 因此在啟動任何其他節點之前必須先啟動主節點。
roscore
啟動主節點啟動主節點的方式是在另一個終端機視窗執行 roscore
指令:
$ roscore
這個指令會啟動一個新的主節點,讓它來負責協調和管理所有節點的通訊。roscore
也會啟動其他核心服務和工具,例如參數伺服器(Parameter server)、 rosout
( 日誌節點) 和 roslaunch
(套件啟動工具)。
參數伺服器(Parameter server):
參數伺服器是 ROS 系統中一個全域的儲存空間,它會儲存全域的配置設定或變數,任一節點都可以讀取或修改這些參數。例如,有一個負責移動機器人的節點和一個負責擷取影像的節點,這兩個節點都需要知道機器人的最大速度,這個「最大速度」參數就可以儲存在參數伺服器中,則不管是哪個節點,都能從同一個地方讀取或更新「最大速度」參數。
rosout
(日誌節點) :
rosout
也是一個特殊節點,負責集中處理和記錄 ROS 系統中的日誌訊息。
roslaunch
(套件啟動工具):
roslaunch
是用來啟動一個套件中多個節點的工具,套件必須先建立 .launch
檔(XML 格式的啟動檔案),則 roslaunch
就會根據啟動設定來依序啟動特定節點。
此時重新執行 create_node.py
,就可以看到程式開始輸出日誌訊息了:
$ python3 create_node.py
[INFO] [1724252218.628211]: Hello ROS world 6
[INFO] [1724252218.629642]: Node is running...
[INFO] [1724252219.629879]: Hello ROS world 9
[INFO] [1724252219.633996]: Node is running...
[INFO] [1724252220.629656]: Hello ROS world 10
[INFO] [1724252220.632700]: Node is running...
rosrun
啟動特定節點我們也可以使用指令 rosrun
從指定的 ROS 套件中執行一個節點,這個方法可以不用特別跑到檔案所在位置來執行檔案(尤其用 C++ 撰寫節點則編譯後執行檔會放在其他地方),並且也不需要指定直譯器(不用再輸入 python3
了)。
格式:
rosrun <package_name> <executable>
Python 撰寫的節點在第一次執行時需要先用 chmod +x
修改權限指令,讓這個檔案成為可執行檔(executable file)。其中chmod
(change mode)這個指令可以用於改變檔案或目錄的存取權限。+x
表示給予檔案增加執行(execute)權限。
以前面建立的 create_node.py
為例:
$ chmod +x create_node.py
$ rosrun my_package create_node.py
[INFO] [1724253103.219538]: Hello ROS world 3
[INFO] [1724253103.221236]: Node is running...
[INFO] [1724253104.220890]: Hello ROS world 4
[INFO] [1724253104.223521]: Node is running...
[INFO] [1724253105.221048]: Hello ROS world 7
[INFO] [1724253105.224848]: Node is running...
注意
rosrun
需要指定套件名稱,因此如果沒有建立套件便無法使用。
現在我們有了第一個 ROS 節點啦!雖然並沒有做太多特別的事情,甚至連機器人的一點邊都沒有碰到,但是沒關係,預計下一篇就會稍微有一點點機器人的內容了。